const Service = require('egg').Service
const ms = require('ms')
const jwt = require('jsonwebtoken')
const { pathToRegexp } = require('path-to-regexp')
const NodeCache = require('node-cache')
const cache = new NodeCache()

class AdminService extends Service {
  /**
   * 系统登录
   * @param {Object} options
   * @param {String} options.username
   * @param {String} options.password
   * @returns
   */
  async login(options = {}, filter) {
    const { ctx, config } = this
    const { helper } = ctx

    if (!filter) {
      helper.validate(options, {
        required: ['username', 'password'],
      })

      filter = {
        username: options.username,
        password: helper.md5(options.password),
      }
    }

    const admin = await ctx.model.Admin.findOne(filter).then((doc) => {
      if (!doc) throw '用户名或密码错误'
      return doc
    })

    const user = await this.getUserInfo(admin.id)

    // 签发token
    const token = jwt.sign(user, config.jwt.secret + 'admin', { expiresIn: config.jwt.expiresIn })

    const expireTime = Date.now() + ms(config.jwt.expiresIn)

    const auth = await this.getRoleAuth(user.roleId)

    return {
      user,

      token,
      expireTime,
      auth,
    }
  }

  /**
   * 获取用户信息
   * @param {*} adminId
   * @param {*} where
   * @returns
   */
  async getUserInfo(adminId) {
    const { ctx } = this
    const { helper } = ctx

    const admin = await ctx.model.Admin.findOne({
      _id: adminId,
    }).then((admin) => {
      if (!admin) throw '用户不存在'
      if (!admin.isOpen) throw '账号状态不正常'

      return admin
    })

    const adminRole = await ctx.model.AdminRole.findOne({
      _id: admin.roleId,
    }).then((adminRole) => {
      if (!adminRole) throw '用户暂未绑定角色'
      return adminRole
    })

    const usreInfo = {
      ...helper.exclude(admin.toJSON(), ['password', 'isDelete']),

      isAdmin: adminRole.isAdmin,
      roleId: adminRole.id,

      systemType: admin.systemType,
      sourceId: admin.sourceId,
    }

    return usreInfo
  }

  /**
   * 验证码用户名
   */
  async verifyUsername(username, adminId) {
    // 账户去重
    const filter = {
      username,
    }

    if (adminId)
      filter._id = {
        $ne: adminId,
      }

    await this.ctx.model.Admin.findOne(filter).then((admin) => {
      if (admin) throw '账号已被使用'
    })
  }

  /**
   * 验证码手机号
   */
  async verifyMobile(mobile, adminId) {
    if (mobile.length != 11) throw '手机号长度不正确'

    // 手机号去重
    const filter = {
      mobile,
    }

    if (adminId)
      filter._id = {
        $ne: adminId,
      }

    await this.ctx.model.Admin.findOne(filter).then((admin) => {
      if (admin) throw '手机号已被使用'
    })
  }

  async userAuth(adminId) {}

  /**
   * 权限校验
   * 判断接口是否有权限
   * @param {Ojbect} admin
   * @param {Ojbect} admin.roleId
   * @param {Boolean} [admin.isAdmin] 是否超级管理员
   * @param {String} method 请求类型
   * @param {String} reqPath 请求路径
   * @returns 是否有权限
   */
  async verifyAuth(admin, method, reqPath, apis = []) {
    // 超级管理员（所有权限）
    if ((admin.isAdmin && admin.systemType == 'admin') || admin.isSuperAdmin) return true

    const roleApis = await this.getRoleApiAuth(admin.roleId)

    apis = [...roleApis, ...apis]

    const matchResult = apis.some((api) => {
      const methodMatch = api.method == method || api.method == '*'
      const regexp = pathToRegexp(api.path[0] == '/' ? api.path : '/backend/' + api.path)

      const pathMatch = regexp.exec(reqPath)

      return methodMatch && pathMatch
    })

    return matchResult
  }

  /**
   * 获取角色API权限
   */
  async getRoleApiAuth(roleId) {
    const cacheKey = this.getApiCacheKey(roleId)

    const cacheData = cache.get(cacheKey)
    if (cacheData) return JSON.parse(cacheData)

    const menus = await this.getRoleMenu(roleId)

    const apis = [
      { method: 'GET', path: 'system/menu/route' },
      { method: 'GET', path: 'system/config/dict' },
      { method: 'GET', path: 'system/config/constant' },
      { method: 'GET', path: 'system/setting' },
      { method: 'GET', path: 'system/auth' },
      { method: 'GET', path: 'system/user/info' },
      { method: 'PUT', path: 'system/user/modify-password' },
    ]

    menus.map((item) =>
      item.apis.map((api) => {
        apis.push({
          method: api.method,
          path: api.path,
        })
      })
    )

    // 缓存秒数
    const ttl = ms('5m') / 1000
    cache.set(cacheKey, JSON.stringify(apis), ttl)

    return apis
  }

  getApiCacheKey(roleId) {
    return `ROLE_API_${roleId}`
  }

  getMenuCacheKey(roleId) {
    return `ROLE_MENU_${roleId}`
  }

  // 更新角色缓存
  async updateCache() {
    // roleId
    // const cacheKey = this.getApiCacheKey(roleId)
    // cache.del(cacheKey)

    const keys = cache.keys()
    keys.length && cache.del(keys)
  }

  /**
   * 角色菜单
   * @param {String} roleId
   */
  async getRoleMenu(roleId) {
    const { ctx } = this

    if (!roleId) throw '角色获取失败'

    const cacheKey = this.getMenuCacheKey(roleId)

    const cacheData = cache.get(cacheKey)
    if (cacheData) return JSON.parse(cacheData)

    const adminRole = await ctx.model.AdminRole.findOne({
      _id: roleId,
    })

    const menuFilter = { isOpen: true }

    // 管理员，拥有当前系统所有权限
    if (adminRole.isAdmin) {
      menuFilter.systemType = adminRole.systemType
    } else {
      menuFilter._id = adminRole.menuIds
    }

    const menus = await ctx.model.AdminMenu.find(menuFilter)

    // 缓存秒数
    const ttl = ms('5m') / 1000
    cache.set(cacheKey, JSON.stringify(menus), ttl)

    return menus
  }

  /**
   * 获取角色权限
   */
  async getRoleAuth(roleId) {
    return this.getRoleMenu(roleId).then((res) => {
      return res.map((item) => {
        return {
          menuName: item.menuName,
          authKey: item.authKey || item.id,
          menuName: item.menuName,
        }
      })
    })
  }
}

module.exports = AdminService
